use clap::Parser;
use std::fs;
use walkdir::WalkDir;

use yk::config::Config;
use yk::executor::{execute_snippet, handle_preview_command};
use yk::init::init_config;
use yk::parser::parse_snippets_from_file;
use yk::ui::run_skim_interface;
use yk::CodeSnippet;

// --- Command Line Interface (CLI) Structure ---
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Option<Commands>,
}

#[derive(clap::Subcommand, Debug)]
enum Commands {
    /// Preview code snippet
    Preview {
        /// File path
        file: String,
        /// Line number
        line: usize,
    },
    /// Save and execute code snippet (keeps temporary file in current directory)
    Save,
    /// Initialize configuration directory and files
    Init,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Parse command line arguments
    let cli = Cli::parse();

    // Handle init subcommand
    if let Some(Commands::Init) = cli.command {
        return init_config();
    }

    // Handle preview subcommand
    if let Some(Commands::Preview { file, line }) = cli.command {
        return handle_preview_command(&file, line);
    }

    // 1. Load configuration only from user's home directory, no longer check project directory
    let config_path = dirs::home_dir()
        .ok_or("Cannot find home directory")?
        .join(".config/yk/config.json");

    if !config_path.exists() {
        eprintln!("Configuration file does not exist, please run 'yk init' to initialize the configuration");
        return Ok(());
    }

    let config_content = fs::read_to_string(&config_path)?;
    let config: Config = serde_json::from_str(&config_content)?;

    // 2. Only look for snippets in the source directory under user's home directory
    let source_dir = dirs::home_dir()
        .ok_or("Cannot find home directory")?
        .join(".config/yk/source");

    if !source_dir.exists() {
        eprintln!(
            "Source directory does not exist, please run 'yk init' to initialize the configuration"
        );
        return Ok(());
    }

    // Collect all raw code snippets (without assigning IDs)
    let mut all_raw_snippets = Vec::new();

    for entry in WalkDir::new(&source_dir).into_iter().filter_map(|e| e.ok()) {
        if entry.path().extension().is_some_and(|ext| ext == "md") {
            match parse_snippets_from_file(entry.path(), &config) {
                Ok(snippets) => all_raw_snippets.extend(snippets),
                Err(_) => continue, // Skip files that cannot be parsed
            }
        }
    }

    if all_raw_snippets.is_empty() {
        return Ok(()); // Return silently, no valid code snippets
    }

    // Uniformly assign IDs: use list index as unique ID
    let all_snippets: Vec<CodeSnippet> = all_raw_snippets
        .into_iter()
        .enumerate()
        .map(|(idx, raw)| CodeSnippet {
            id: idx, // Use index as ID, starting from 0
            tag: raw.tag,
            code: raw.code,
            language: raw.language,
            file_path: raw.file_path,
            line_number: raw.line_number,
            end_line: raw.end_line,
        })
        .collect();

    // Handle save subcommand - now it has access to config and snippets
    if let Some(Commands::Save) = cli.command {
        // Run Skim interface and execute selected code in save mode
        if let Some(selected_snippet) = run_skim_interface(all_snippets, &config) {
            if let Err(e) = execute_snippet(&selected_snippet, &config, true) {
                eprintln!("Error executing code: {}", e);
            }
        }
        return Ok(());
    }

    // 3. Run Skim interface and execute selected code (normal mode)
    if let Some(selected_snippet) = run_skim_interface(all_snippets, &config) {
        if let Err(e) = execute_snippet(&selected_snippet, &config, false) {
            eprintln!("Error executing code: {}", e);
        }
    }

    Ok(())
}
